home *** CD-ROM | disk | FTP | other *** search
- /**************************************************************************
- * *
- * Copyright (c) 1991 Silicon Graphics, Inc. *
- * All Rights Reserved *
- * *
- * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF SGI *
- * *
- * The copyright notice above does not evidence any actual of intended *
- * publication of such source code, and is an unpublished work by Silicon *
- * Graphics, Inc. This material contains CONFIDENTIAL INFORMATION that is *
- * the property of Silicon Graphics, Inc. Any use, duplication or *
- * disclosure not specifically authorized by Silicon Graphics is strictly *
- * prohibited. *
- * *
- * RESTRICTED RIGHTS LEGEND: *
- * *
- * Use, duplication or disclosure by the Government is subject to *
- * restrictions as set forth in subdivision (c)(1)(ii) of the Rights in *
- * Technical Data and Computer Software clause at DFARS 52.227-7013, *
- * and/or in similar or successor clauses in the FAR, DOD or NASA FAR *
- * Supplement. Unpublished - rights reserved under the Copyright Laws of *
- * the United States. Contractor is SILICON GRAPHICS, INC., 2011 N. *
- * Shoreline Blvd., Mountain View, CA 94039-7311 *
- **************************************************************************
- *
- * File: SLUtil.c
- *
- * Description: This file contains misc. utility functions grlobally
- * available to internal SL routines.
- *
- **************************************************************************/
-
-
- #ident "$Revision: 1.11 $"
-
-
- #include <stdio.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <errno.h>
- #ifdef sgi
- #include <bstring.h>
- #endif
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <sys/time.h>
- #include <signal.h>
- #include <ctype.h>
- #include "spoolI.h"
-
-
- /* SLExec spooler output buffer globals */
-
- char **_SLspooler_out_buf; /* Output buffer text */
- int _SLspooler_nout; /* # lines in output buffer */
- int _SLspooler_exit; /* Spooler exit status */
-
-
- /* External functions needing declarations */
-
- extern int select(int, fd_set*, fd_set*, fd_set*, struct timeval*);
-
-
- /* Local functions */
-
- static void spooler_buf_init(void);
- static void spooler_buf_add(char*);
- static int read_ready(int, int);
-
-
- /**************************************************************************
- *
- * Function: _SLIsEmpty
- *
- * Description: Determines if a string is empty or filled only with
- * whitespace.
- *
- * Parameters:
- * str (I) - string to test
- *
- * Return: 0 not empty. 1 if string is empty.
- *
- **************************************************************************/
-
- int _SLIsEmpty(register const char *str)
- {
- if (str == NULL)
- return 1;
- for (; *str; str++)
- if (!isspace((int)*str))
- return 0;
- return 1;
- }
-
-
- /**************************************************************************
- *
- * Function: _SLSkipSpace
- *
- * Description: Skips whitespace and returns a pointer to the first
- * non-whitespace character or NULL if there is only whitespace
- * on the line.
- *
- * Parameters:
- * sbuf (I) - string buffer to parse for whitespace.
- *
- * Return: Pointer to first non-whitespace character or NULL if the
- * line consists only of whitespace characters;
- *
- **************************************************************************/
-
- char *_SLSkipSpace(char *sbuf)
- {
- char *sptr = NULL;
-
- while (*sbuf) {
- if (!isspace(*sbuf)) {
- sptr = sbuf;
- break;
- }
- sbuf++;
- }
-
- return sptr;
- }
-
-
- /**************************************************************************
- *
- * Function: _SLInitPlist
- *
- * Description: Initializes a printer list.
- *
- * Parameters:
- * plistp (O) - pointer to printer list.
- * nump (O) - pointer to number of printers on list.
- *
- * Return: none
- *
- **************************************************************************/
-
- void _SLInitPlist(SLPrinterStruct **plistp, int *nump)
- {
- register int i;
- register SLPrinterStruct *ptr;
-
- if (*plistp) {
- /*
- * Free the storage for each printer. If this becomes a
- * performance problem we can go to free-lists.
- */
- for (i = 0, ptr = *plistp; i < *nump; i++, ptr++)
- _SLInitPentry(ptr);
-
- /*
- * Free the list storage
- */
- free((char*)*plistp);
- *plistp = NULL;
- }
- *nump = 0;
- }
-
-
- /**************************************************************************
- *
- * Function: _SLInitPentry
- *
- * Description: Initializes a printer info entry. All storage alocated int
- * the SLPrinterStruct is freed.
- *
- * Parameters:
- * pentry (O) - pointer to printer info structure.
- *
- * Return: none
- *
- **************************************************************************/
-
- void _SLInitPentry(SLPrinterStruct *pentry)
- {
- if (pentry) {
- if (pentry->local_name) {
- free((char*)pentry->local_name);
- pentry->local_name = NULL;
- }
- if (pentry->formal_name) {
- free((char*)pentry->formal_name);
- pentry->formal_name = NULL;
- }
- if (pentry->type) {
- free((char*)pentry->type);
- pentry->type = NULL;
- }
- if (pentry->dev) {
- free((char*)pentry->dev);
- pentry->dev = NULL;
- }
- if (pentry->remote_host) {
- free((char*)pentry->remote_host);
- pentry->remote_host = NULL;
- }
- if (pentry->remote_name) {
- free((char*)pentry->remote_name);
- pentry->remote_name = NULL;
- }
- if (pentry->network_type) {
- free(pentry->network_type);
- pentry->network_type = NULL;
- }
- }
- }
-
-
- /**************************************************************************
- *
- * Function: _SLAddPrinter
- *
- * Description: Adds a printer to the specified printer list
- *
- * Parameters:
- * pinfo (I) - pointer to the printer info struct to add to the list
- * plistp (O) - pointer to printer list.
- * nump (O) - pointer to number of printers on list.
- *
- * Return: None
- *
- **************************************************************************/
-
- void _SLAddPrinter(const SLPrinterStruct *pinfo, SLPrinterStruct **plistp,
- int *nump)
- {
- (*nump)++;
- if (*plistp)
- /*
- * We realloc for each printer we add. Can realloc in chunks
- * if this is a performance problem.
- */
- *plistp = (SLPrinterStruct*)realloc(*plistp,
- (*nump) * sizeof(SLPrinterStruct));
- else
- *plistp = (SLPrinterStruct*)malloc((*nump) * sizeof(SLPrinterStruct));
- (*plistp)[*nump - 1] = *pinfo;
- }
-
-
- /**************************************************************************
- *
- * Function: _SLInitQueue
- *
- * Description: Initializes a printer queue.
- *
- * Parameters:
- * queuep (O) - pointer to queue.
- * nump (O) - pointer to number of jobs in queue.
- *
- * Return: none
- *
- **************************************************************************/
-
- void _SLInitQueue(SLQueueStruct **queuep, int *nump)
- {
- int i;
- SLQueueStruct *qptr;
-
- if (*queuep) {
- /*
- * Free the storage for each job. If this becomes a
- * performance problem we can go to free-lists.
- */
- for (i = 0, qptr = *queuep; i < *nump; i++, qptr++)
- _SLFreeQueueEntry(qptr);
-
- /*
- * Free the list storage
- */
- free((char*)*queuep);
- *queuep = NULL;
- }
- *nump = 0;
- }
-
-
- /**************************************************************************
- *
- * Function: _SLFreeQueueEntry
- *
- * Description: Frees the storage for a single queue entry
- *
- * Parameters:
- * entry (I) - pointer to the entry whose storage is to be freed
- *
- * Return: none
- *
- **************************************************************************/
-
- void _SLFreeQueueEntry(SLQueueStruct *entry)
- {
- if (entry->job_id)
- free((char*)entry->job_id);
- if (entry->username)
- free((char*)entry->username);
- if (entry->title)
- free(entry->title);
- }
-
-
- /**************************************************************************
- *
- * Function: _SLAddQueue
- *
- * Description: Adds a job to the specified queue
- *
- * Parameters:
- * queuep (O) - pointer to queue
- * nump (O) - pointer to number of jobs in queue.
- *
- * Return: Pointer to newly added queue structure.
- *
- **************************************************************************/
-
- SLQueueStruct* _SLAddQueue(SLQueueStruct **queuep, int *nump)
- {
- (*nump)++;
- if (*queuep)
- /*
- * We realloc for each job we add. Can realloc in chunks
- * if this is a performance problem.
- */
- *queuep = (SLQueueStruct*)realloc(*queuep,
- (*nump) * sizeof(SLQueueStruct));
- else
- *queuep = (SLQueueStruct*)malloc((*nump) * sizeof(SLQueueStruct));
- return &(*queuep)[*nump - 1];
- }
-
-
- /**************************************************************************
- *
- * Function: _SLExec
- *
- * Description: Executes the specified command and returns the exit
- * status. The command output and the number of lines of output
- * are stored in globals for use in error reporting and ID
- * parsing.
- *
- * Parameters:
- * job_source (I) - specifies the source of a print job. This is
- * specified for print job submittal. Otherwise it may be
- * set to NULL.
- * cmd (I) - command to execute
- * timeout_flag (I) - TRUE == use select with a timeout; FALSE ==
- * do not use select (i.e. no timeouts).
- * timeout_resp (O) - Timeout result. TRUE if timed out, FALSE if
- * did not timeout. Pointer can be NULL if
- * timeout_flag is not TRUE.
- *
- * Return: Exit status of the specified command.
- *
- **************************************************************************/
-
- int _SLExec(SLJobSourceUnion *job_source, char *cmd, int timeout_flag,
- int *timeout_resp)
- {
- char buf[SL_BUFSIZ];
- FILE *pptr;
- int status;
- int ret, ready_ret, pid, read_pipe[2];
- int write_pipe[2];
- int infd;
- struct sigaction sact, orig_sact;
- sigset_t sig_mask;
-
- /*
- * First free the old output buffer if any
- */
- spooler_buf_init();
-
- /*
- * Initialize the write pipe
- */
- write_pipe[0] = write_pipe[1] = 0;
-
- /*
- * Initialize the input file descriptor. If this is non-zero in
- * the child process, we'll dup it to standard input.
- */
- infd = 0;
-
- /*
- * If appropriate specify a file descriptor for the command to read
- * from on its standard input.
- *
- * If a file descriptor is specified simply tell the command to read
- * from that descriptor.
- *
- * If a buffer has been specified we will open a pipe and specify
- * the pipe's read end as the file descriptor for the command to
- * read as its stdin. We will write the buffer to the write end of
- * the pipe.
- */
- (void)strcpy(buf, cmd);
- if (job_source) {
- switch(job_source->type) {
- case SL_JOB_FILENAME:
- if (SLdebug)
- (void)fprintf(stderr,
- gettxt(_SGI_LIBSPOOL_DEBUG_FILENAME_JOB,
- "libspool Debug: _SLExec filename job\n"));
- break;
- case SL_JOB_FD:
- if (SLdebug)
- (void)fprintf(stderr,
- gettxt(_SGI_LIBSPOOL_DEBUG_FD_JOB,
- "libspool Debug: _SLExec fd job\n"));
- infd = job_source->fd_job.file_desc;
- break;
- case SL_JOB_BUF:
- if (SLdebug)
- (void)fprintf(stderr,
- gettxt(_SGI_LIBSPOOL_DEBUG_BUF_JOB,
- "libspool Debug: _SLExec buffer job\n"));
- if (pipe(write_pipe) < 0) {
- (void)fprintf(stderr, gettxt(_SGI_LIBSPOOL_PIPE_FAILED,
- "libspool INTERNAL ERROR: _SLExec pipe failed (errno=%d)\n"),
- errno);
- return SL_ERROR;
- }
- infd = write_pipe[0];
- break;
- default:
- break;
- }
- }
-
- /*
- * Print some debug info if requested
- */
- if (SLdebug)
- (void)fprintf(stderr, gettxt(_SGI_LIBSPOOL_DEBUG_EXECCMD,
- "libspool Debug: _SLExec command \"%s\"\n"), cmd);
-
- /*
- * Before we execute the command we need to default SIGCLD so that
- * when the command terminates we don not call a user's SIGCLD
- * handler. Do not worry about losing SIGCLD for the user because
- * if we get one during our ignore period, the user's child will
- * zombie and sigaction will raise a new SIGCLD if it detects a
- * zombie when we reinstall the original handler.
- */
- sigemptyset(&sig_mask);
- sact.sa_handler = SIG_DFL;
- sact.sa_mask = sig_mask;
- sact.sa_flags = 0;
- (void)sigaction(SIGCLD, &sact, &orig_sact);
-
- /*
- * Execute the command (adapted from popen)
- *
- * We fork a shell that executes the speicified command. The standard
- * out of the command has been connected to the write end of a pipe
- * that our parent process reads. For convenience we fdopen a stream
- * based on the read end of the pipe.
- */
- if (pipe(read_pipe) < 0) {
- (void)sigaction(SIGCLD, &orig_sact, NULL);
- (void)fprintf(stderr, gettxt(_SGI_LIBSPOOL_PIPE_FAILED,
- "libspool INTERNAL ERROR: _SLExec pipe failed (errno=%d)\n"),
- errno);
- return SL_ERROR;
- }
- if ((pid = fork()) == 0) {
- /*
- * Make child a process group leader so that if we timeout
- * we can kill the child's pgroup and have all its children killed.
- * This is especially important when dealing with rsh since it is
- * flakey.
- */
- (void)setpgrp();
-
- /*
- * Set up the pipes that the child will use to communicate with
- * the parent.
- */
- if (infd) {
- (void)dup2(infd, 0);
- (void)close(infd);
- }
- if (write_pipe[1]) /* If writing to command */
- (void)close(write_pipe[1]); /* close child write end */
- (void)close(read_pipe[0]); /* Close child read end */
- if (read_pipe[1] != 1) { /* If pipe not already stdout */
- (void)close(1); /* Close child stdout */
- (void)fcntl(read_pipe[1], F_DUPFD, 1); /* Make pipe chld out */
- (void)close(read_pipe[1]); /* Close our pipe write */
- }
-
- /*
- * Set up the environment so that we get output in the
- * language we can parse and in the error message format
- * we require.
- */
- putenv(SL_LANG);
- putenv(SL_MSGFORMAT);
-
- /*
- * Execute the child command
- */
- (void)execl(SL_CMD_SH, SL_CMD_SH_NAME, SL_CMD_SH_FLAG, buf, NULL);
- exit(1);
- }
- if (pid == -1) {
- (void)sigaction(SIGCLD, &orig_sact, NULL);
- (void)fprintf(stderr, gettxt(_SGI_LIBSPOOL_FORK_FAILED,
- "libspool INTERNAL ERROR: _SLExec fork failed (errno=%d)\n"),
- errno);
- return SL_ERROR;
- }
-
- /*
- * Set up the pipes that will be used to communicate with
- * the child
- */
- if (write_pipe[0]) /* If writing to command */
- (void)close(write_pipe[0]); /* close parent read end */
- (void)close(read_pipe[1]); /* Close parent write end */
- /* Create a stream for the pipe */
- if ((pptr = fdopen(read_pipe[0], "r")) == NULL) {
- (void)fprintf(stderr, gettxt(_SGI_LIBSPOOL_FDOPEN_FAILED,
- "libspool INTERNAL ERROR: fdopen failed\n"));
- return SL_ERROR;
- }
-
- /*
- * If the command is to be fed from a buffer, feed it
- */
- if (write_pipe[1]) {
- (void)write(write_pipe[1], job_source->buf_job.buffer,
- job_source->buf_job.amount);
- (void)close(write_pipe[1]);
- }
-
- /*
- * Read the output from the command and build the output buffer
- */
- while ((ready_ret = read_ready(read_pipe[0], timeout_flag)) != 0) {
- if (fgets(buf, SL_SML_BUFSIZ, pptr) == NULL)
- break;
- buf[strlen(buf)-1] = '\0';
- spooler_buf_add(buf);
- }
-
- /*
- * Close our end of the pipe to the child
- */
- (void)fclose(pptr);
-
- /*
- * If the we have a timeout condition, explicitly kill the
- * child's process group and set the timeout response
- * (if not NULL pointer). The child is a process group leader
- * and killing its group kills any children that may have been
- * forked.
- */
- if (ready_ret == 0) {
- if (SLdebug)
- (void)fprintf(stderr, gettxt(_SGI_LIBSPOOL_DEBUG_TIMEOUT,
- "libspool Debug: _SLExec timeout - killing pid %d\n"), pid);
- (void)kill(-pid, SIGKILL);
- }
- if (timeout_resp)
- *timeout_resp = (ready_ret) ? SL_FALSE: SL_TRUE;
-
- /*
- * Wait for the child process executing the command to finish
- * and save the command exit status. This is adapted from pclose.
- */
- #ifdef sgi
- while (((ret = waitpid(pid, &status, 0)) < 0) && oserror() == EINTR)
- ;
- #else
- while (((ret = waitpid(pid, &status, 0)) < 0) && errno == EINTR)
- ;
- #endif
- _SLspooler_exit = ((ret < 0) || (ready_ret == 0)) ?
- SL_ERROR : WEXITSTATUS(status);
-
- /*
- * Restore the original SIGCLD handler. If there are zombies
- * out there a SIGCLD will be raised and the user's desired action
- * will be performed.
- */
- (void)sigaction(SIGCLD, &orig_sact, NULL);
-
- /*
- * If the command exit code was not 0 but we do not have any
- * spooler error message, put some generic message in the output
- * buffer so that apps get some sort of error text.
- */
- if (_SLspooler_exit != 0 && _SLspooler_nout == 0) {
- spooler_buf_add(gettxt(_SGI_LIBSPOOL_SPOOLER_CMD_ERR_1,
- "Spooler command execution error."));
- spooler_buf_add(gettxt(_SGI_LIBSPOOL_SPOOLER_CMD_ERR_2,
- "Filenames or other input data may"));
- spooler_buf_add(gettxt(_SGI_LIBSPOOL_SPOOLER_CMD_ERR_3,
- "be improperly specified."));
- }
-
- return _SLspooler_exit;
- }
-
-
- /*
- ========================================================================
- LOCAL FUNCTIONS
- ========================================================================
- */
-
-
- /**************************************************************************
- *
- * Function: spooler_buf_init
- *
- * Description: Initializes the spooler output message buffer global
- * variables.
- *
- * Parameters: none
- *
- * Return: none
- *
- **************************************************************************/
-
- static void spooler_buf_init(void)
- {
- register int i;
-
- if (_SLspooler_nout) {
- for (i = 0; i < _SLspooler_nout; i++)
- free((char*)_SLspooler_out_buf[i]);
- free((char*)_SLspooler_out_buf);
- _SLspooler_out_buf = NULL;
- _SLspooler_nout = 0;
- }
-
- }
-
-
- /**************************************************************************
- *
- * Function: spooler_buf_add
- *
- * Description: Adds the specified text line to the spooler output message
- * buffer.
- *
- * Parameters:
- * str (I) - line of text to add to buffer
- *
- * Return: none
- *
- **************************************************************************/
-
- static void spooler_buf_add(char *str)
- {
- _SLspooler_nout++;
- if (_SLspooler_out_buf)
- _SLspooler_out_buf = (char**)realloc((char*)_SLspooler_out_buf,
- _SLspooler_nout * sizeof(char*));
- else
- _SLspooler_out_buf = (char**)malloc(sizeof(char*));
- _SLspooler_out_buf[_SLspooler_nout-1] = strdup(str);
- }
-
-
- /**************************************************************************
- *
- * Function: read_ready
- *
- * Description: Does a select on the read end of the child process output
- * pipe. The select will timeout if there is some problem (e.g.
- * rsh hanging) or will return normally indicating we have output
- * ready to read.
- *
- * Parameters:
- * read_fd (I) - fd to select on for read
- * do_select (I) - TRUE == do the timed select. FALSE == simply
- * return a ready indication.
- *
- * Return: 1 is returned if we are ready to read. 0 is returned if select
- * has timed out.
- *
- **************************************************************************/
-
- static int read_ready(int read_fd, int do_select)
- {
- fd_set fdset;
- struct timeval timeout;
- int ret, rv = 0, again = 1;
-
- /*
- * Return immediately with a ready if select not requested
- */
- if (do_select == SL_FALSE)
- return 1;
-
- /*
- * Run select in a loop in case the call is interrupted by a
- * signal (EINTR). This way we can reissue the select.
- */
- while (again) {
- /*
- * Clear the descriptor array and set our descriptor
- * for select
- */
- FD_ZERO(&fdset);
- FD_SET(read_fd, &fdset);
-
- /*
- * Set a timeout. Note that we do this in the loop per
- * select man page recommendation (see BUGS section in
- * man page).
- */
- timeout.tv_sec = SLnet_timeout;
- timeout.tv_usec = 0;
-
- /*
- * Issue the select and handle the response. If a timeout
- * occurs return with 0. If select returns normally, return
- * a 1. If select is interrupted, don't return but do
- * another select. If select returns with any other error
- * call it a draw and return 0.
- */
- ret = select(read_fd + 1, &fdset, (fd_set*)NULL,
- (fd_set*)NULL, &timeout);
- again = 0;
- if (ret < 0 && errno == EINTR)
- again = 1;
- else if (ret > 0)
- rv = 1;
- }
-
- return rv;
- }
-